package partFour;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/* 本类用于使用线程池改写售票案例*/
public class TestRunnablevPoolV2 {
    public static void main(String[] args) {
        //1.创建实现类也就是目标业务对象
        TicktRunnablePool2 target = new TicktRunnablePool2();
        //2.使用Excutors创建最多包含5个线程的线程池--ExcutorService
        /*Executors是用来辅助创建线程池的工具类
         * 常用的方法是：newFixedThreadPoo(int)这个方法可以帮我们创建指定书目线程的线程池*/
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //3.使用循环，启动线程
        for(int i=0; i<5; i++){
            /*execute让线程池中的线程来执行业务
             * 每次调用这个方法，都会将一个线程加到就绪队列中
             * 这个方法的参数，就是我们要执行的具体业务，也就是目标业务类对象target*/
            pool.execute(target);
        }
    }
}

//1.创建接口实现类，作为目标业务量
class TicktRunnablePool2 implements Runnable{
    //3.1定义票数
    int tickets = 100;
    //8.注意在外部添加一个唯一的对象，
    Object o = new Object();
    //2.添加父接口中未实现的抽象方法，里面是业务
    @Override
    public void run() {
        //3.2循环卖票
        while (true) {
            /*
             * 8.同步代码块：synchronized(锁对象){会出现安全隐患的所有代码}
             * 同步代码块在同一时刻，同一资源只会被一个线程独享
             * synchronized (new Object()){ 这种写法不对，相当于每个线程进来的时候都会new一个锁对象，线程间使用的并不是同一把锁
             * 修改同步代码块的锁对象为成员变量o,因为锁对象必须唯一
             * synchronized (TicketRunnable2.class) { 这种写法也是可以的
             * */
            synchronized (o){
                //9.双重判断，有票的时候再卖票，解决超卖
                if (tickets>0){
                    //7.为了增加线程状态切换概率与出错频率，在售票前休眠10毫秒
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                System.out.println("窗口" + Thread.currentThread().getName() + "售票" + tickets--);
                if (tickets <= 0) break;
            }
            }
        }
    }
}